package services

import (
	"context"
	"fmt"
	"github.com/pkg/errors"
	"github.com/prometheus/common/model"
	"github.com/spf13/viper"
	"go.didapinche.com/foundation/plat-echo/app/ops/models"
	"go.didapinche.com/foundation/plat-echo/app/ops/models/request"
	"go.didapinche.com/foundation/plat-echo/app/ops/models/response"
	"go.didapinche.com/foundation/plat-echo/app/ops/repositories"
	"go.didapinche.com/foundation/plat-echo/app/ops/zclients"
	promentheus "go.didapinche.com/foundation/plat-echo/pkg/prometheus"
	"go.didapinche.com/foundation/plat-echo/pkg/utils/data"
	"go.didapinche.com/goapi/user_department_service_api"
	dtime "go.didapinche.com/time"
	"go.uber.org/zap"
	"strconv"
	"strings"
	"sync"
	"time"
)

type ApiDataService interface {
	CollectDataHttp(ctx context.Context) error //收集数据
	CollectDataZeus(ctx context.Context) error //收集数据
	Find(ctx context.Context, request *request.ApiSlaRequest) ([]*response.ApiData, int, error)
	FindSla(ctx context.Context, request *request.ApiSlaRequest) (*response.ApiData, error)
	GetStatistics(ctx context.Context, start, end, groupID, name string, level uint64, size int) (*response.Statistics, error)
	FindSlaStat(ctx context.Context, req *request.OverviewReq) (*response.SlaStat, error)
	FindGraphByIsForce(ctx context.Context, id uint64) ([]*models.Graph, error)

	//清理api重复数据
	ClearnApiData(ctx context.Context, start string) error
}

var apiMap sync.Map

type apiDataService struct {
	apiService         ApiService
	department         user_department_service_api.TChanDepartmentService
	repository         repositories.ApiDataRepository
	apiRepository      repositories.ApiRepository
	historyRepository  repositories.AlertHistoryRepository
	appRepository      repositories.ApplicationRepository
	graphRepository    repositories.GraphRepository
	templateRepository repositories.TemplateRepository
	logger             *zap.Logger
	prome              *promentheus.PrometheusService
}

func NewApiDataServic(
	templateRepository repositories.TemplateRepository,
	graphRepository repositories.GraphRepository,
	repository repositories.ApiDataRepository,
	logger *zap.Logger,
	apiService ApiService,
	department user_department_service_api.TChanDepartmentService,
	historyRepository repositories.AlertHistoryRepository,
	apiRepository repositories.ApiRepository,
	appRepository repositories.ApplicationRepository,
	prome *promentheus.PrometheusService,
) ApiDataService {
	return &apiDataService{
		templateRepository: templateRepository,
		graphRepository:    graphRepository,
		repository:         repository,
		department:         department,
		appRepository:      appRepository,
		historyRepository:  historyRepository,
		apiRepository:      apiRepository,
		apiService:         apiService,
		logger:             logger.With(zap.String("type", "services.ApiData")),
		prome:              prome,
	}
}

//
func (s apiDataService) CollectDataHttp(ctx context.Context) error {
	s.logger.Info("start apiDataService CollectDataHttp")
	apps, err := s.appRepository.FindAll(ctx)
	if err != nil {
		s.logger.Error("call app CollectDataHttp error", zap.Error(err))
		return errors.Wrap(err, "call app CollectDataHttp error")
	}
	s.logger.Debug("收集数据查询app结束")
	apiDatas, err := s.queryExpress(ctx, apps, viper.GetString("api.http.qps"))
	if err != nil {
		s.logger.Error("call apiDataService.queryExpress error" + viper.GetString("api.http.qps"))
		return errors.Wrap(err, "call apiDataService.queryExpress error")
	}
	s.logger.Debug("收集数据查询qps结束")
	m := make(map[string]*models.ApiData)
	for _, api := range apiDatas {
		key := api.AppName + ":" + api.HttpMethod + ":" + api.HttpPath
		api.QPS = api.Value
		api.Type = 0
		m[key] = api
	}
	apiDatas2, err := s.queryExpress(ctx, apps, viper.GetString("api.http.tp95"))
	if err != nil {
		s.logger.Error("call apiDataService.queryExpress error" + viper.GetString("api.http.tp95"))
	}
	s.logger.Debug("收集数据查询tp95结束")
	for _, api := range apiDatas2 {
		key := api.AppName + ":" + api.HttpMethod + ":" + api.HttpPath
		if v, ok := m[key]; ok {
			v.TP95 = api.Value
		}
	}
	apiDatas3, err := s.queryExpress(ctx, apps, viper.GetString("api.http.tp99"))
	if err != nil {
		s.logger.Error("call apiDataService.queryExpress error" + viper.GetString("api.http.tp99"))
	}
	s.logger.Debug("收集数据查询tp99结束")
	for _, api := range apiDatas3 {
		key := api.AppName + ":" + api.HttpMethod + ":" + api.HttpPath
		if v, ok := m[key]; ok {
			v.TP99 = api.Value
		}
	}
	apiDatas4, err := s.queryExpress(ctx, apps, viper.GetString("api.http.requestCount"))
	if err != nil {
		s.logger.Error("call apiDataService.queryExpress error" + viper.GetString("api.http.requestCount"))
	}
	s.logger.Debug("收集数据查询请求数结束")
	for _, api := range apiDatas4 {
		key := api.AppName + ":" + api.HttpMethod + ":" + api.HttpPath
		if v, ok := m[key]; ok {
			v.TotalRequestCount = api.Value
		}
	}
	apiDatas5, err := s.queryExpress(ctx, apps, viper.GetString("api.http.ErrorCount"))
	if err != nil {
		s.logger.Error("call apiDataService.queryExpress error" + viper.GetString("api.http.ErrorCount"))
	}
	s.logger.Debug("收集数据查询错误数结束")
	for _, api := range apiDatas5 {
		key := api.AppName + ":" + api.HttpMethod + ":" + api.HttpPath
		if v, ok := m[key]; ok {
			v.ErrorCount = api.Value
		}
	}
	if err := s.repository.Creates(ctx, apiDatas); err != nil {
		s.logger.Error("call repository.Creates error", zap.Error(err))
	} else {
		s.logger.Debug("收集数据创建数据结束")
	}
	s.logger.Info("start apiDataService CollectDataHttp")
	return nil
}

func (s apiDataService) CollectDataZeus(ctx context.Context) error {
	apps, err := s.appRepository.FindAll(ctx)
	if err != nil {
		s.logger.Error("call app CollectDataHttp error", zap.Error(err))
		return errors.Wrap(err, "call app CollectDataHttp error")
	}
	s.logger.Info("start apiDataService CollectDataZeus")
	apiDatas, err := s.queryExpress(ctx, apps, viper.GetString("api.zeus.qps"))
	if err != nil {
		s.logger.Error("call apiDataService.queryExpress error")
		return errors.Wrap(err, "call apiDataService.queryExpress error")
	}
	m := make(map[string]*models.ApiData)
	for _, api := range apiDatas {
		key := api.ZeusService + ":" + api.ZeusMethod
		api.QPS = api.Value
		api.Type = 1
		m[key] = api
	}
	apiDatas2, err := s.queryExpress(ctx, apps, viper.GetString("api.zeus.tp95"))
	if err != nil {
		s.logger.Error("call apiDataService.queryExpress error")
	}
	for _, api := range apiDatas2 {
		key := api.ZeusService + ":" + api.ZeusMethod
		if v, ok := m[key]; ok {
			v.TP95 = api.Value
		}
	}
	apiDatas3, err := s.queryExpress(ctx, apps, viper.GetString("api.zeus.tp99"))
	if err != nil {
		s.logger.Error("call apiDataService.queryExpress error")
	}
	for _, api := range apiDatas3 {
		key := api.ZeusService + ":" + api.ZeusMethod
		if v, ok := m[key]; ok {
			v.TP99 = api.Value
		}
	}
	apiDatas4, err := s.queryExpress(ctx, apps, viper.GetString("api.zeus.requestCount"))
	if err != nil {
		s.logger.Error("call apiDataService.queryExpress error")
	}
	for _, api := range apiDatas4 {
		key := api.ZeusService + ":" + api.ZeusMethod
		if v, ok := m[key]; ok {
			v.TotalRequestCount = api.Value
		}
	}
	apiDatas5, err := s.queryExpress(ctx, apps, viper.GetString("api.zeus.ErrorCount"))
	if err != nil {
		s.logger.Error("call apiDataService.queryExpress error:" + viper.GetString("api.zeus.ErrorCount"))
	}
	for _, api := range apiDatas5 {
		key := api.ZeusService + ":" + api.ZeusMethod
		if v, ok := m[key]; ok {
			v.ErrorCount = api.Value
		}
	}
	apiDatas6, err := s.queryExpress(ctx, apps, viper.GetString("api.zeus.ClientErrorCount"))
	if err != nil {
		s.logger.Error("call apiDataService.queryExpress error")
	}
	for _, api := range apiDatas6 {
		key := api.ZeusService + ":" + api.ZeusMethod
		if v, ok := m[key]; ok {
			v.ClientErrorCount = api.Value
		}
	}
	if err := s.repository.Creates(ctx, apiDatas); err != nil {
		s.logger.Error("call repository.Creates error", zap.Error(err))
		return errors.Wrap(err, "call repository.Creates error")
	}

	s.logger.Info("end apiDataService CollectDataZeus")
	return nil
}

func (s apiDataService) queryExpress(ctx context.Context, apps []*models.Application, express string) ([]*models.ApiData, error) {
	appMap := make(map[string]*models.Application)
	for _, app := range apps {
		appMap[app.Name] = app
	}
	result, err := s.prome.QueryVectorProm(express, time.Now())
	if err != nil {
		s.logger.Error("call prome.QueryVectorProm error")
		return nil, errors.Wrap(err, "call prome.QueryVectorProm error")
	}
	apiDatas := make([]*models.ApiData, 0)
	switch result.(type) {
	case model.Vector:
		if vector, ok := result.(model.Vector); ok {
			for _, v := range vector {
				apidata := &models.ApiData{
					AppName:     string(v.Metric["app"]),
					Date:        v.Timestamp.String(),
					HttpPath:    string(v.Metric["uri"]),
					ZeusService: string(v.Metric["service"]),
					Value:       float64(v.Value),
				}
				if apidata.AppName != "" {
					if app, ok := appMap[apidata.AppName]; ok {
						apidata.AppID = app.ID
						apidata.DevGroupID = app.DevGroupID
						apidata.DevGroupName = app.DevGroupName
						apidata.Level = app.Level
						apidata.Owner = app.Owner
					} else {
						s.logger.Info("跳过一个暂时未收集到app信息的数据:" + string(v.Metric["app"]))
						continue
					}
				} else {
					s.logger.Info("跳过一个暂时未收集到app信息的数据:" + string(v.Metric["app"]))
					continue
				}
				if apidata.ZeusService != "" {
					apidata.ZeusMethod = string(v.Metric["method"])
				}
				if apidata.HttpPath != "" {
					apidata.HttpMethod = string(v.Metric["method"])
				}
				if apidata.HttpPath != "" {
					key := apidata.AppName + ":" + apidata.HttpMethod + ":" + apidata.HttpPath
					if v, ok := apiMap.Load(key); ok {
						if api, ok := v.(*models.Api); ok {
							apidata.ApiID = api.ID
						} else {
							api, err := s.apiRepository.FindByHttpPath(ctx, apidata.AppName, apidata.HttpMethod, apidata.HttpPath)
							if err == nil && api != nil {
								apidata.ApiID = api.ID
								apiMap.Store(key, api)
							} else {
								continue
								//api := new(models.Api)
								//api.AppName = apidata.AppName
								//api.Owner = apidata.Owner
								//api.Level = apidata.Level
								//api.DevGroupName = apidata.DevGroupName
								//api.DevGroupID = apidata.DevGroupID
								//api.AppID = apidata.AppID
								//api.HttpMethod = apidata.HttpMethod
								//api.HttpPath = apidata.HttpPath
								//api.Type = 0
								//api, err := s.apiService.Create(ctx, api)
								//if err != nil {
								//	s.logger.Error("call apiService Create error", zap.Error(err))
								//	continue
								//} else {
								//	apidata.ApiID = api.ID
								//	apiMap.Store(key, api)
								//}
							}
						}
					} else {
						api, err := s.apiRepository.FindByHttpPath(ctx, apidata.AppName, apidata.HttpMethod, apidata.HttpPath)
						if err == nil && api != nil {
							s.logger.Debug("查询api:" + key)
							apidata.ApiID = api.ID
							apiMap.Store(key, api)
						} else {
							continue
							//api := new(models.Api)
							//api.AppName = apidata.AppName
							//api.Owner = apidata.Owner
							//api.Level = apidata.Level
							//api.DevGroupName = apidata.DevGroupName
							//api.DevGroupID = apidata.DevGroupID
							//api.AppID = apidata.AppID
							//api.HttpMethod = apidata.HttpMethod
							//api.HttpPath = apidata.HttpPath
							//api.Type = 0
							//api, err := s.apiService.Create(ctx, api)
							//if err != nil {
							//	s.logger.Error("call apiService Create error", zap.Error(err))
							//	continue
							//} else {
							//	apidata.ApiID = api.ID
							//	apiMap.Store(key, api)
							//}
						}
					}
				} else {
					key := apidata.AppName + ":" + apidata.ZeusService + ":" + apidata.ZeusMethod
					if v, ok := apiMap.Load(key); ok {
						if api, ok := v.(*models.Api); ok {
							apidata.ApiID = api.ID
						} else {
							api, err := s.apiRepository.FindByZeus(ctx, apidata.AppName, apidata.ZeusService, apidata.ZeusMethod, 1)
							if err == nil && api != nil {
								apidata.ApiID = api.ID
								apiMap.Store(key, api)
							} else {
								//api := new(models.Api)
								//api.AppName = apidata.AppName
								//api.Owner = apidata.Owner
								//api.Level = apidata.Level
								//api.DevGroupName = apidata.DevGroupName
								//api.DevGroupID = apidata.DevGroupID
								//api.AppID = apidata.AppID
								//api.ZeusMethod = apidata.ZeusMethod
								//api.ZeusService = apidata.ZeusService
								//api.Type = 1
								//api, err := s.apiService.Create(ctx, api)
								//if err != nil {
								//	s.logger.Error("call apiService Create error", zap.Error(err))
								//	continue
								//} else {
								//	apidata.ApiID = api.ID
								//	apiMap.Store(key, api)
								//}
							}
						}
					} else {
						api, err := s.apiRepository.FindByZeus(ctx, apidata.AppName, apidata.ZeusService, apidata.ZeusMethod, 1)
						if err == nil && api != nil {
							apidata.ApiID = api.ID
							apiMap.Store(key, api)
						} else {
							//api := new(models.Api)
							//api.AppName = apidata.AppName
							//api.Owner = apidata.Owner
							//api.Level = apidata.Level
							//api.DevGroupName = apidata.DevGroupName
							//api.DevGroupID = apidata.DevGroupID
							//api.AppID = apidata.AppID
							//api.ZeusMethod = apidata.ZeusMethod
							//api.ZeusService = apidata.ZeusService
							//api.Type = 1
							//api, err := s.apiService.Create(ctx, api)
							//if err != nil {
							//	s.logger.Error("call apiService Create error", zap.Error(err))
							//	continue
							//} else {
							//	apidata.ApiID = api.ID
							//	apiMap.Store(key, api)
							//}
						}
					}
				}
				apidata.CreateTime = dtime.Now()
				apiDatas = append(apiDatas, apidata)
			}
		}

	}
	return apiDatas, nil
}
func (s apiDataService) FindGraphByIsForce(ctx context.Context, id uint64) ([]*models.Graph, error) {
	graphs, err := s.graphRepository.FindByApiIDAndIsForce(ctx, id)
	if err != nil {
		s.logger.Error("call graphRepository FindByApiIDAndIsForce error")
		return nil, errors.Wrap(err, "call graphRepository FindByApiIDAndIsForce error")
	}
	api, err := s.apiRepository.FindByID(ctx, id)
	if err != nil {
		s.logger.Error("call apiRepository FindByID error")
		return nil, errors.Wrap(err, "call apiRepository FindByID error")
	}
	for _, g := range graphs {
		g.Status = 1
		if api.DisableGraph != nil && len(api.DisableGraph.GraphID) > 0 {
			if data.ContainInt(api.DisableGraph.GraphID, int(g.ID)) {
				g.Status = 0
			}
		}
	}
	return graphs, nil
}

func (s apiDataService) Find(ctx context.Context, request *request.ApiSlaRequest) ([]*response.ApiData, int, error) {
	start, err := time.ParseInLocation("2006-01-02 15:04:05", request.Start, time.Local)
	if err != nil {
		s.logger.Error("call time ParseInLocation error")
		return nil, 0, err
	}
	end, err := time.ParseInLocation("2006-01-02 15:04:05", request.End, time.Local)
	if err != nil {
		s.logger.Error("call time ParseInLocation error")
		return nil, 0, err
	}
	base := float64(end.Sub(start).Milliseconds())
	// 查询历史告警
	alertChan := make(chan []*response.ApiData, 1)
	go func() {
		s.logger.Debug("查询sla模版")
		template, err := s.templateRepository.FindByIsSla(ctx)
		if err != nil {
			s.logger.Error("call templateRepository FindSla error")
		}
		IDs := make([]uint64, 0)
		for _, t := range template {
			if request.TemplateType == 1 {
				if strings.Contains(strings.ToUpper(t.Name), "HTTP") {
					IDs = append(IDs, t.ID)
				}
			} else if request.TemplateType == 2 {
				if strings.Contains(strings.ToUpper(t.Name), "ZEUS") {
					IDs = append(IDs, t.ID)
				}
			}
		}
		request.TemplateIds = IDs
		if request.AppName != "" || request.Query != "" {
			apis, err := s.apiRepository.Find(ctx, request.AppName, request.Query, request.TemplateType-1)
			if err != nil {
				s.logger.Error("call apiRepository Find error", zap.Error(err))
			} else {
				for _, api := range apis {
					request.ApiIDs = append(request.ApiIDs, api.ID)
				}
			}
		}
		s.logger.Debug("告警sla查询")
		if (request.AppName != "" || request.Query != "") && len(request.ApiIDs) == 0 {
			alertChan <- nil
		} else {
			alert, err := s.historyRepository.FindApiByDate(ctx, request)
			if err != nil {
				s.logger.Error("call historyRepository FindApiByDate error")
			}
			alertChan <- alert
		}
	}()
	countChan := make(chan int, 1)
	dataChan := make(chan []*response.ApiData, 1)
	go func() {
		s.logger.Debug("查询data")
		data, count, err := s.repository.FindList(ctx, request)
		if err != nil {
			s.logger.Error("call repository FindList error")
		}
		dataChan <- data
		s.logger.Debug("向通道写入data")
		countChan <- count
		s.logger.Debug("向通道写入count")
	}()

	apiChan := make(chan []*models.Api, 1)
	go func() {
		s.logger.Debug("查询api")
		api, err := s.apiRepository.FindByDisableGraph(ctx)
		if err != nil {
			s.logger.Error("call apiRepository FindByDisableGraph error")
		}
		apiChan <- api
	}()
	alert := <-alertChan
	s.logger.Debug("完成alert查询")
	disableGraph := <-apiChan
	s.logger.Debug("完成api查询")
	disable := make(map[uint64]*models.Api)
	for _, api := range disableGraph {
		disable[api.ID] = api
	}
	count := <-countChan
	s.logger.Debug("完成非sla的排序查询" + strconv.Itoa(count))
	if !strings.Contains(request.Order, "sla") {
		m := make(map[uint64]*response.ApiData)
		for _, a := range alert {
			m[a.ApiID] = a
		}
		data2 := <-dataChan
		for _, d := range data2 {
			d.Owners = d.GetOwners()
			if request.TemplateType == 1 {
				d.Method = d.HttpPath
			} else {
				d.Method = d.ZeusMethod
			}
			if d.HttpPath == "" {
				d.HttpPath = "/"
			}
			d.Status = 1

			if _, ok := disable[d.ApiID]; ok {
				if disable[d.ApiID].DisableGraph != nil && len(disable[d.ApiID].DisableGraph.GraphID) > 0 {
					d.Status = -1
					if len(disable[d.ApiID].DisableGraph.GraphID) == 5 {
						d.Status = 0
					}
				}
			}
			if v, ok := m[d.ApiID]; ok {
				d.SLA = v.SLA
			}
			d.QPS = float64(data.Wrap(d.QPS, 0))
			d.QPSMax = float64(data.Wrap(d.QPSMax, 0))
			d.TotalRequestCount = float64(data.Wrap(d.TotalRequestCount, 0))
			d.ClientTotalRequestCount = float64(data.Wrap(d.ClientTotalRequestCount, 0))
			d.ClientErrorCount = float64(data.Wrap(d.ClientErrorCount, 0))
			d.ErrorCount = float64(data.Wrap(d.ErrorCount, 0))
			d.ID = d.ApiID
			d.TemplateType = request.TemplateType
			d.SLA = Compute(d.SLA, base)
		}
		return data2, count, err
	} else {
		s.logger.Debug("开始sla排序查询")
		pageNum, pageSize := data.ComputePage(len(alert), int(request.PageNum), int(request.PageSize))
		if pageSize == 0 && pageNum == 0 {
			s.logger.Debug("获得告警sla的内容")
			alerts := alert[(request.PageNum-1)*request.PageSize : request.PageSize]
			request.ApiIDs = request.ApiIDs[0:0]
			for _, a := range alerts {
				request.ApiIDs = append(request.ApiIDs, a.ApiID)
			}
			s.logger.Debug("获得告警sla的ApiID")
			data2, err := s.repository.FindByApiIDs(ctx, request)
			if err != nil {
				s.logger.Error("call repository.FindByApiIDs error", zap.Error(err))
				return nil, 0, errors.Wrap(err, "call repository.FindByApiIDs error")
			}
			s.logger.Debug("告警slaAppID查询成功")
			dataMap := make(map[uint64]*response.ApiData)
			for _, d := range data2 {
				dataMap[d.ApiID] = d
			}
			s.logger.Debug("排序中")
			for _, a := range alerts {
				mergeData(a, dataMap[a.ApiID])
				a.TemplateType = request.TemplateType
				a.ID = a.ApiID
				a.Status = 1
				a.Owners = a.GetOwners()
				s.mergeApi(ctx, a)
				a.QPS = float64(data.Wrap(a.QPS, 0))
				a.QPSMax = float64(data.Wrap(a.QPSMax, 0))
				a.TotalRequestCount = float64(data.Wrap(a.TotalRequestCount, 0))
				a.ClientTotalRequestCount = float64(data.Wrap(a.ClientTotalRequestCount, 0))
				a.ClientErrorCount = float64(data.Wrap(a.ClientErrorCount, 0))
				a.ErrorCount = float64(data.Wrap(a.ErrorCount, 0))
				a.SLA = Compute(a.SLA, base)
				if _, ok := disable[a.ApiID]; ok {
					if disable[a.ApiID].DisableGraph != nil && len(disable[a.ApiID].DisableGraph.GraphID) > 0 {
						a.Status = -1
						if len(disable[a.ApiID].DisableGraph.GraphID) == 5 {
							a.Status = 0
						}
					}
				}
			}
			return alerts, count, nil
		} else if pageNum == 0 && pageSize != 0 {
			s.logger.Debug("告警sla需要和data合并")
			alerts := alert[(request.PageNum-1)*request.PageSize : len(alert)]
			request.ApiIDs = request.ApiIDs[0:0]
			for _, a := range alerts {
				a.Owners = a.GetOwners()
				request.ApiIDs = append(request.ApiIDs, a.ApiID)
			}
			s.logger.Debug("告警sla查询appIDS")
			data2, err := s.repository.FindByApiIDs(ctx, request)
			if err != nil {
				s.logger.Error("call repository.FindByApiIDs error", zap.Error(err))
				return nil, 0, errors.Wrap(err, "call repository.FindByApiIDs error")
			}
			request.PageNum = int64(pageNum)
			request.PageSize = int64(pageSize)
			if request.PageSize <= 0 {
				request.PageSize = 20
			}
			if request.PageNum <= 0 {
				request.PageNum = 0
			}
			s.logger.Debug("告警sla查询list2")
			data3, err := s.repository.FindList2(ctx, request)
			if err != nil {
				s.logger.Error("call repository.FindByApiIDs error", zap.Error(err))
				return nil, 0, errors.Wrap(err, "call repository.FindList2 error")
			}
			s.logger.Debug("合并告警sla查询list2")
			alerts = append(alerts, data3...)
			dataMap := make(map[uint64]*response.ApiData)
			for _, d := range data2 {
				dataMap[d.ApiID] = d
			}
			for _, a := range alerts {
				mergeData(a, dataMap[a.ApiID])
				a.TemplateType = request.TemplateType
				a.ID = a.ApiID
				a.SLA = Compute(a.SLA, base)
				a.Owners = a.GetOwners()
				s.mergeApi(ctx, a)
				a.Status = 1
				a.QPS = float64(data.Wrap(a.QPS, 0))
				a.QPSMax = float64(data.Wrap(a.QPSMax, 0))
				a.TotalRequestCount = float64(data.Wrap(a.TotalRequestCount, 0))
				a.ClientTotalRequestCount = float64(data.Wrap(a.ClientTotalRequestCount, 0))
				a.ClientErrorCount = float64(data.Wrap(a.ClientErrorCount, 0))
				a.ErrorCount = float64(data.Wrap(a.ErrorCount, 0))
				if _, ok := disable[a.ApiID]; ok {
					if disable[a.ApiID].DisableGraph != nil && len(disable[a.ApiID].DisableGraph.GraphID) > 0 {
						a.Status = -1
						if len(disable[a.ApiID].DisableGraph.GraphID) == 5 {
							a.Status = 0
						}
					}
				}
			}
			return alerts, count, nil
		} else {
			s.logger.Debug("sla查询list")
			request.PageNum = int64(pageNum)
			request.PageSize = int64(pageSize)
			if request.PageSize <= 0 {
				request.PageSize = 20
			}
			if request.PageNum <= 0 {
				request.PageNum = 0
			}
			data2, err := s.repository.FindList2(ctx, request)
			if err != nil {
				s.logger.Error("call repository.FindByApiIDs error", zap.Error(err))
				return nil, 0, errors.Wrap(err, "call repository.FindList2 error")
			}
			for _, a := range data2 {
				a.TemplateType = request.TemplateType
				a.ID = a.ApiID
				a.QPS = float64(data.Wrap(a.QPS, 0))
				a.QPSMax = float64(data.Wrap(a.QPSMax, 0))
				a.TotalRequestCount = float64(data.Wrap(a.TotalRequestCount, 0))
				a.ClientTotalRequestCount = float64(data.Wrap(a.ClientTotalRequestCount, 0))
				a.ClientErrorCount = float64(data.Wrap(a.ClientErrorCount, 0))
				a.ErrorCount = float64(data.Wrap(a.ErrorCount, 0))
				a.Status = 1
				a.SLA = Compute(a.SLA, base)
				a.Owners = a.GetOwners()
				if _, ok := disable[a.ApiID]; ok {
					if disable[a.ApiID].DisableGraph != nil && len(disable[a.ApiID].DisableGraph.GraphID) > 0 {
						a.Status = -1
						if len(disable[a.ApiID].DisableGraph.GraphID) == 5 {
							a.Status = 0
						}
					}
				}
			}
			return data2, count, nil
		}
	}
}

func (s apiDataService) mergeApi(ctx context.Context, data *response.ApiData) {
	if data.TemplateType == 1 && data.HttpPath == "" {
		api, err := s.apiRepository.FindByID(ctx, data.ApiID)
		if err != nil {
			s.logger.Error("call apiRepository FindByID error", zap.Error(err))
			return
		}
		data.AppName = api.AppName
		data.Owners = api.GetOwners()
		data.HttpPath = api.HttpPath
		data.HttpMethod = api.HttpMethod
		data.Level = uint64(api.Level)
		data.GroupID = api.DevGroupID
	} else if data.TemplateType == 1 && data.ZeusMethod == "" {
		api, err := s.apiRepository.FindByID(ctx, data.ApiID)
		if err != nil {
			s.logger.Error("call apiRepository FindByID error", zap.Error(err))
			return
		}
		data.AppName = api.AppName
		data.Owners = api.GetOwners()
		data.ZeusMethod = api.ZeusMethod
		data.ZeusService = api.ZeusService
		data.Level = uint64(api.Level)
		data.GroupID = api.DevGroupID
	}
}

func mergeData(data1 *response.ApiData, data2 *response.ApiData) {
	if data1 != nil && data2 != nil {
		data1.QPS = data2.QPS
		data1.QPSMax = data2.QPSMax
		data1.HttpPath = data2.HttpPath
		data1.HttpMethod = data2.HttpMethod
		data1.GroupID = data2.GroupID
		data1.Value = data2.Value
		data1.ZeusService = data2.ZeusService
		data1.ZeusMethod = data2.ZeusMethod
		data1.AppID = data2.AppID
		data1.Owner = data2.Owner
		data1.AppName = data2.AppName
		data1.ErrorCount = data2.ErrorCount
		data1.TP95 = data2.TP95
		data1.TP95Max = data2.TP95Max
		data1.TP99 = data2.TP99
		data1.TP99Max = data2.TP99Max
		data1.Owners = data2.GetOwners()
		data1.Level = data2.Level
		data1.Method = data2.Method
		data1.ClientErrorCount = data2.ClientErrorCount
		data1.ClientTotalRequestCount = data2.ClientTotalRequestCount
		data1.TotalRequestCount = data2.TotalRequestCount
	}
}

//计算sla
func Compute(sla, base float64) float64 {

	if base-sla < 0 {
		return 0
	}
	value, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", 100*(base-sla)/base), 64)
	return value
}

func (s apiDataService) FindSla(ctx context.Context, request *request.ApiSlaRequest) (*response.ApiData, error) {
	start, err := time.ParseInLocation("2006-01-02 15:04:05", request.Start, time.Local)
	if err != nil {
		s.logger.Error("call time ParseInLocation error")
		return nil, err
	}
	end, err := time.ParseInLocation("2006-01-02 15:04:05", request.End, time.Local)
	if err != nil {
		s.logger.Error("call time ParseInLocation error")
		return nil, err
	}

	base := float64(end.Sub(start).Milliseconds())
	data, err := s.repository.FindByDate(ctx, dtime.Time{start}, dtime.Time{end}, request.AppName, request.Query, request.TemplateType-1)
	if err != nil {
		s.logger.Error("call apiDataRepository Find error", zap.Error(err))
		return nil, errors.Wrap(err, "call apiDataRepository Find error")
	}
	apis, err := s.apiRepository.Find(ctx, request.AppName, request.Query, request.TemplateType-1)
	if err != nil {
		return nil, errors.Wrap(err, "call apiRepository Find error")
	}
	apiIDs := make([]uint64, 0)
	if request.AppName != "" || request.Query != "" {
		for _, api := range apis {
			apiIDs = append(apiIDs, api.ID)
		}
	}
	data2, err := s.historyRepository.FindApiSumSla(ctx, dtime.Time{start}, dtime.Time{end}, apiIDs, request.TemplateType-1)
	if err != nil {
		s.logger.Error("call apiDataRepository Find error", zap.Error(err))
		return nil, errors.Wrap(err, "call apiDataRepository Find error")
	}
	data.SLA = Compute(data2.SLA, base)
	data.QPS, _ = strconv.ParseFloat(fmt.Sprintf("%.0f", data.QPS), 64)
	data.TotalRequestCount, _ = strconv.ParseFloat(fmt.Sprintf("%.0f", data.TotalRequestCount), 64)
	data.ErrorCount, _ = strconv.ParseFloat(fmt.Sprintf("%.0f", data.ErrorCount), 64)
	return data, err
}

func (s apiDataService) GetStatistics(ctx context.Context, start, end, groupID, name string, level uint64, size int) (*response.Statistics, error) {
	start2, err := time.ParseInLocation("2006-01-02 15:04:05", start, time.Local)
	if err != nil {
		s.logger.Error("call time ParseInLocation error")
		return nil, err
	}
	end2, err := time.ParseInLocation("2006-01-02 15:04:05", end, time.Local)
	if err != nil {
		s.logger.Error("call time ParseInLocation error")
		return nil, err
	}

	base := float64(end2.Sub(start2).Milliseconds())
	groupIDs, err := zclients.GetNodeIDs(ctx, s.department, groupID)
	if err != nil {
		s.logger.Error("call zclients.GetNodeIDs error", zap.Error(err))
		return nil, errors.Wrap(err, "call zclients.GetNodeIDs error")
	}
	result := new(response.Statistics)
	switch name {
	case "api_http_sla":
		apiDatas, err := s.repository.FindStatisticsSla(ctx, dtime.Time{start2}, dtime.Time{end2}, "sum(h.date) value", groupIDs, level, size, 0)
		if err != nil {
			s.logger.Error("call repository.FindStatisticsSla error", zap.Error(err))
			return nil, errors.Wrap(err, "call repository.FindStatisticsSla error")
		}
		result.Data = apiDatas
	case "api_zeus_server_sla":
		apiDatas, err := s.repository.FindStatisticsSla(ctx, dtime.Time{start2}, dtime.Time{end2}, "sum(h.date) value", groupIDs, level, size, 1)
		if err != nil {
			s.logger.Error("call repository.FindStatisticsSla error", zap.Error(err))
			return nil, errors.Wrap(err, "call repository.FindStatisticsSla error")
		}
		result.Data = apiDatas
	case "api_http_error_count":
		apiDatas, err := s.repository.FindStatistics(ctx, dtime.Time{start2}, dtime.Time{end2}, "sum(error_count) value", groupIDs, level, size, 0)
		if err != nil {
			s.logger.Error("call repository.FindStatistics error", zap.Error(err))
			return nil, errors.Wrap(err, "call repository.FindStatistics error")
		}
		result.Data = apiDatas
	case "api_http_tp99":
		apiDatas, err := s.repository.FindStatistics(ctx, dtime.Time{start2}, dtime.Time{end2}, "max(tp99) value", groupIDs, level, size, 0)
		if err != nil {
			s.logger.Error("call repository.FindStatistics error", zap.Error(err))
			return nil, errors.Wrap(err, "call repository.FindStatistics error")
		}
		result.Data = apiDatas
	case "api_http_qps":
		apiDatas, err := s.repository.FindStatistics(ctx, dtime.Time{start2}, dtime.Time{end2}, "max(qps) value", groupIDs, level, size, 0)
		if err != nil {
			s.logger.Error("call repository.FindStatistics error", zap.Error(err))
			return nil, errors.Wrap(err, "call repository.FindStatistics error")
		}
		result.Data = apiDatas

	case "api_zeus_server_error_count":
		apiDatas, err := s.repository.FindStatistics(ctx, dtime.Time{start2}, dtime.Time{end2}, "sum(error_count) value", groupIDs, level, size, 1)
		if err != nil {
			s.logger.Error("call repository.FindStatistics error", zap.Error(err))
			return nil, errors.Wrap(err, "call repository.FindStatistics error")
		}
		result.Data = apiDatas

	case "api_zeus_server_tp99":
		apiDatas, err := s.repository.FindStatistics(ctx, dtime.Time{start2}, dtime.Time{end2}, "max(tp99) value", groupIDs, level, size, 1)
		if err != nil {
			s.logger.Error("call repository.FindStatistics error", zap.Error(err))
			return nil, errors.Wrap(err, "call repository.FindStatistics error")
		}
		result.Data = apiDatas
	case "api_zeus_server_qps":
		apiDatas, err := s.repository.FindStatistics(ctx, dtime.Time{start2}, dtime.Time{end2}, "max(qps) value", groupIDs, level, size, 1)
		if err != nil {
			s.logger.Error("call repository.FindStatistics error", zap.Error(err))
			return nil, errors.Wrap(err, "call repository.FindStatistics error")
		}
		result.Data = apiDatas
	}

	result.Name = name
	//保留小数
	for i, _ := range result.Data {
		if strings.Contains(result.Name, "tp99") {
			if result.Data[i].Value >= 1 {
				result.Data[i].Value, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", result.Data[i].Value), 64)
				result.Data[i].Value2 = fmt.Sprint(result.Data[i].Value) + "s"
			} else {
				result.Data[i].Value, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", result.Data[i].Value*1000), 64)
				result.Data[i].Value2 = fmt.Sprint(result.Data[i].Value) + "ms"
			}
			continue
		}
		if strings.Contains(result.Name, "qps") || strings.Contains(result.Name, "count") {
			result.Data[i].Value, _ = strconv.ParseFloat(fmt.Sprintf("%.0f", result.Data[i].Value), 64)
			result.Data[i].Value2 = fmt.Sprint(result.Data[i].Value)
			continue
		}
		if strings.Contains(result.Name, "sla") {
			if base == 0 || base-result.Data[i].Value < 0 {
				result.Data[i].Value = 0
			} else {
				result.Data[i].Value = (base - result.Data[i].Value) / base
			}
			result.Data[i].Value, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", result.Data[i].Value*100), 64)
			result.Data[i].Value2 = fmt.Sprint(result.Data[i].Value) + "%"
			continue
		} else {
			result.Data[i].Value, _ = strconv.ParseFloat(fmt.Sprintf("%.0f", result.Data[i].Value), 64)
			result.Data[i].Value2 = fmt.Sprint(result.Data[i].Value)
		}
	}

	return result, nil
}

func (s apiDataService) FindSlaStat(ctx context.Context, req *request.OverviewReq) (*response.SlaStat, error) {
	start, err := time.ParseInLocation("2006-01-02 15:04:05", req.Start, time.Local)
	if err != nil {
		return nil, err
	}
	end, err := time.ParseInLocation("2006-01-02 15:04:05", req.End, time.Local)
	if err != nil {
		return nil, err
	}
	req.StartDate = start
	req.EndDate = end
	groupIDs, err := zclients.GetNodeIDs(ctx, s.department, req.GroupID)
	if err != nil {
		s.logger.Error("call apiDataService FindSlaStat error", zap.Error(err))
		return nil, errors.Wrap(err, "call apiDataService FindSlaStat error")
	}
	slaStat := new(response.SlaStat)
	slaStat.L0 = new(response.StatItem)
	slaStat.L1 = new(response.StatItem)
	slaStat.L2 = new(response.StatItem)
	slaStat.L3 = new(response.StatItem)
	slaStat.L4 = new(response.StatItem)
	slaStat.L5 = new(response.StatItem)
	slaStat.L0.SLA = 100
	slaStat.L1.SLA = 100
	slaStat.L2.SLA = 100
	slaStat.L3.SLA = 100
	slaStat.L4.SLA = 100
	slaStat.L5.SLA = 100

	app, err := s.appRepository.FindCountByLevel(ctx, groupIDs)
	if err != nil {
		s.logger.Error("call appRepository FindCountByLevel error", zap.Error(err))
		return nil, errors.Wrap(err, "call appRepository FindCountByLevel error")
	}
	s.changerSlaStat(slaStat, app, 0)
	s.logger.Debug(fmt.Sprint(groupIDs))
	httpData, err := s.repository.FindConutByHttpAndData(ctx, groupIDs, dtime.Time{req.StartDate}, dtime.Time{req.EndDate})
	if err != nil {
		s.logger.Error("call apiDataRepository FindConutByHttpAndData error", zap.Error(err))
		return nil, errors.Wrap(err, "call apiDataRepository FindConutByHttpAndData error")
	}
	s.changerSlaStat(slaStat, httpData, 1)
	zeusData, err := s.repository.FindConutByZeusAndData(ctx, groupIDs, dtime.Time{req.StartDate}, dtime.Time{req.EndDate})
	if err != nil {
		s.logger.Error("call apiDataRepository FindConutByZeusAndData error", zap.Error(err))
		return nil, errors.Wrap(err, "call apiDataRepository FindConutByZeusAndData error")
	}
	s.changerSlaStat(slaStat, zeusData, 2)
	templates, err := s.templateRepository.FindByIsSla(ctx)
	if err != nil {
		s.logger.Error("call templateRepository FindByIsSla error", zap.Error(err))
		return nil, errors.Wrap(err, "call templateRepository FindByIsSla error")
	}
	templateIDs := make([]uint64, 0)
	for _, h := range templates {
		templateIDs = append(templateIDs, h.ID)
	}
	slaData, err := s.historyRepository.FindSlaByLevel(ctx, groupIDs, templateIDs, dtime.Time{req.StartDate}, dtime.Time{req.EndDate})
	if err != nil {
		s.logger.Error("call historyRepository FindSlaByLevel error", zap.Error(err))
		return nil, errors.Wrap(err, "call historyRepository FindSlaByLevel error")
	}
	base := float64(req.EndDate.Sub(req.StartDate).Milliseconds())
	for _, sla := range slaData {
		sla.SLA = Compute(sla.SLA, base)
	}
	s.changerSlaStat(slaStat, slaData, 3)
	return slaStat, nil
}

//sla排行榜数据转化，0：转化appCount数据,1:转化http数据 2:转化zeus数据 3：转化sla数据
func (s apiDataService) changerSlaStat(slaStat *response.SlaStat, statItem2 []*response.StatItem, kind int) {
	for _, stat := range statItem2 {
		if stat.Level == 1 {
			s.changerStatItem(slaStat.L0, stat, kind)
		} else if stat.Level == 2 {
			s.changerStatItem(slaStat.L1, stat, kind)
		} else if stat.Level == 3 {
			s.changerStatItem(slaStat.L2, stat, kind)
		} else if stat.Level == 4 {
			s.changerStatItem(slaStat.L3, stat, kind)
		} else if stat.Level == 5 {
			s.changerStatItem(slaStat.L4, stat, kind)
		} else if stat.Level == 6 {
			s.changerStatItem(slaStat.L5, stat, kind)
		}
	}

}

func (s apiDataService) changerStatItem(statItem1, statItem2 *response.StatItem, kind int) {
	if kind == 0 {
		statItem1.AppCount = statItem2.AppCount
		statItem1.Level = statItem2.Level
	} else if kind == 1 {
		statItem1.HttpApiCount = statItem2.HttpApiCount
		statItem1.HttpQPS = float64(data.Wrap(statItem2.HttpQPS, 0))
		statItem1.HttpQPSMax = float64(data.Wrap(statItem2.HttpQPSMax, 0))
		statItem1.HttpErrorCount = float64(data.Wrap(statItem2.HttpErrorCount, 0))
		statItem1.HttpRequestCount = float64(data.Wrap(statItem2.HttpRequestCount, 0))
	} else if kind == 2 {
		statItem1.ZeusApiCount = statItem2.ZeusApiCount
		statItem1.ZeusQPS = float64(data.Wrap(statItem2.ZeusQPS, 0))
		statItem1.ZeusQPSMax = float64(data.Wrap(statItem2.ZeusQPSMax, 0))
		statItem1.ZeusErrorCount = float64(data.Wrap(statItem2.ZeusErrorCount, 0))
		statItem1.ZeusRequestCount = float64(data.Wrap(statItem2.ZeusRequestCount, 0))
	} else if kind == 3 {
		statItem1.SLA = statItem2.SLA
	}

}

func (s apiDataService) ClearnApiData(ctx context.Context, start string) error {
	apis, err := s.apiRepository.FindRepeat(ctx)
	if err != nil {
		return err
	}
	arr := make([]uint64, 0)
	for _, api := range apis {
		arr = append(arr, api.ID)
	}
	if err := s.apiRepository.DeleteByIDs(ctx, arr); err != nil {
		return err
	}
	if err := s.graphRepository.DeleteByApiID(ctx, arr); err != nil {
		return err
	}
	if err := s.repository.DeleteByApiIDsAndTime(ctx, arr, start); err != nil {
		return err
	}
	return nil
}
